#pragma once

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/vector.hpp>

#include <windows.h>
#include <GL/gl.h>
#include "glext.h"

class Util {

public:
	
	// load an opengl extension. Throws an exception if the
	// extension cannot be loaded
	static PROC getGlProc(const char *procName);

	// read an entire file into a string. It is the caller's
	// responsibility to delete the returned pointer!
	static char *readEntireFile(const char *filename);

	// verifies the result of glGetError is what the user is
	// expecting. Calls verifyGlError with GL_NO_ERROR
	static void verifyGlError();

	// verifies the current GL error is equal to expected.
	// Thorws an exception otherwise.
	static void verifyGlError(GLenum expected);

	// make rotation matrix
	static boost::numeric::ublas::matrix<double> MakeRotate(double angle, double ux, double uy, double uz);

	// make rotation matrix in place using the reference parameter
	static void MakeRotateInPlace(boost::numeric::ublas::matrix<double> &mat, double angle, double ux, double uy, double uz);

	// make euler matrix
	static boost::numeric::ublas::matrix<double> MakeEulerMat(double h, double p, double r);

	// makes euler matrix in place
	static void MakeEulerMatInPlace(boost::numeric::ublas::matrix<double> &m, double h, double p, double r);

	// make translation matrix
	static boost::numeric::ublas::matrix<double> MakeTranslateMat(double x, double y, double z);

	// clamp a number between two values
	template <class T>
	static T clamp(T val, T min, T max) {
		if(val < min) {
			return min;
		} else if(val > max) {
			return max;
		}
		return val;
	}

	// load all openGL extensions
	static void LoadExtensions();

	// compute the cross product of two 3-dimensional vectors
	static boost::numeric::ublas::vector<double> cross_product(const boost::numeric::ublas::vector<double> &v1,
		const boost::numeric::ublas::vector<double> &v2);

	static void RedirectIOToConsole();

	// throw an exception if the hresult is FAILED
	static void assert_hr(HRESULT hr);

	// compute the next highest power of 2
	static unsigned int NextHighestPowerOf2(unsigned int val);

public:
	static PFNGLBLENDEQUATIONPROC glBlendEquation;
};

/* Matrix inversion routine.
    Uses lu_factorize and lu_substitute in uBLAS to invert a matrix */
template<class T>
bool InvertMatrix(const ublas::matrix<T> &input, ublas::matrix<T> &inverse) {
 	typedef permutation_matrix<std::size_t> pmatrix;
 	// create a working copy of the input
 	matrix<T> A(input);
 	// create a permutation matrix for the LU-factorization
 	pmatrix pm(A.size1());
 	// perform LU-factorization
 	int res = lu_factorize(A,pm);
        if( res != 0 ) return false;
 	// create identity matrix of "inverse"
 	inverse.assign(ublas::identity_matrix<T>(A.size1()));
 	// backsubstitute to get the inverse
 	lu_substitute(A, pm, inverse);
 	return true;
}